﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.ADF;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Server;
using ESRI.ArcGIS.SystemUI;
using System.ComponentModel;
using ESRI.ArcGIS.Geometry;
using PlanningGIS.MapSheet;
using PlanningGIS.ArcGIS;
using ESRI.ArcGIS.esriSystem;
using System.Reflection;
using System.Dynamic;
using System.IO;
using PlanningGIS.Util.Regular;

namespace PlanningGIS.Framework
{
    /// <summary>
    /// 结合表操作类
    /// </summary>
    public class JHBHelper
    {
        /// <summary>
        /// SysData中所有的结合表的比例尺
        /// </summary>
        private static List<int> _ScaleValues = null;

        /// <summary>
        /// 获取SysData中所有的结合表的比例尺
        /// </summary>
        public static List<int> ScaleValues
        {
            get
            {
                if (_ScaleValues == null || _ScaleValues.Count == 0) GetList();
                return _ScaleValues;
            }
        }
        
        /// <summary>
        /// SysData中所有的结合表图层
        /// </summary>
        private static List<IFeatureClass> _FeatureClasses = null;
        
        /// <summary>
        /// 获取SysData中所有的结合表图层
        /// </summary>
        public static List<IFeatureClass> FeatureClasses
        {
            get
            {
                if (_FeatureClasses == null || _FeatureClasses.Count == 0) GetList();                    
                return _FeatureClasses;
            }
        }

        /// <summary>
        /// 图层字段的索引
        /// </summary>
        private static Dictionary<string, Dictionary<string, int>> _Indexes = null;

        /// <summary>
        /// 获取SysData中所有的结合表图层及比例尺
        /// </summary>
        private static void GetList()
        {            
            string path = AppConfig.DataPath + "\\SysData.gdb";
            if (!Directory.Exists(path)) return;
            IWorkspaceFactory _WksFac = new FileGDBWorkspaceFactory();
            if (!_WksFac.IsWorkspace(path)) return;
            IWorkspace _Wks = _WksFac.OpenFromFile(path, 0);
            if (_Wks == null) return;
            List<IFeatureClass> featcls = GeodatabaseHelper.GetAllFeatureClass(_Wks as IDataset, "结合表");
            if (featcls == null || featcls.Count == 0) return;
            _ScaleValues = new List<int>();
            _FeatureClasses = new List<IFeatureClass>();
            _Indexes = new Dictionary<string, Dictionary<string, int>>();
            foreach (IFeatureClass f in featcls)
            {
                string name = ((IDataset)f).Name;
                if (!name.Contains("结合表")) continue;
                string scale = name.Replace("结合表", "");
                if (string.IsNullOrWhiteSpace(scale) || !VerifyHelper.IsScale(scale)) continue;
                _ScaleValues.Add(int.Parse(scale));
                _FeatureClasses.Add(f);
                Dictionary<string, int> _idx = new Dictionary<string, int>();
                _idx.Add(SheetHelper.F_MapNo, f.FindField(SheetHelper.F_MapNo));
                _idx.Add(SheetHelper.F_LeftIn, f.FindField(SheetHelper.F_LeftIn));
                _idx.Add(SheetHelper.F_LeftTop, f.FindField(SheetHelper.F_LeftTop));
                _idx.Add(SheetHelper.F_LeftUnder, f.FindField(SheetHelper.F_LeftUnder));
                _idx.Add(SheetHelper.F_RightIn, f.FindField(SheetHelper.F_RightIn));
                _idx.Add(SheetHelper.F_RightTop, f.FindField(SheetHelper.F_RightTop));
                _idx.Add(SheetHelper.F_RightUnder, f.FindField(SheetHelper.F_RightUnder));
                _idx.Add(SheetHelper.F_TopIn, f.FindField(SheetHelper.F_TopIn));
                _idx.Add(SheetHelper.F_UnderIn, f.FindField(SheetHelper.F_UnderIn));
                _Indexes.Add(name, _idx);
            }
            _ScaleValues.Sort();
        }

        /// <summary>
        /// 获取指定比例尺的结合表要素类
        /// </summary>
        /// <param name="scale"></param>
        /// <returns></returns>
        public static IFeatureClass GetFeatureClass(int scale)
        {
            if (FeatureClasses == null || FeatureClasses.Count == 0) return null;
            return FeatureClasses.FirstOrDefault(t => (t as IDataset).Name.Equals("结合表" + scale.ToString(), StringComparison.CurrentCultureIgnoreCase));
        }

        /// <summary>
        /// 根据图号获取指定的记录
        /// </summary>
        /// <param name="cls"></param>
        /// <param name="th"></param>
        /// <returns></returns>
        private static IFeature GetFeatureByCode(IFeatureClass cls, string th)
        {
            if (cls == null) return null;
            int thIdx = cls.Fields.FindField(SheetHelper.F_MapNo);
            if (thIdx < 0) return null;
            ISpatialFilter flt = new SpatialFilter();
            flt.WhereClause = string.Format("{0}='{1}'", SheetHelper.F_MapNo, th.ToUpper());
            IFeatureCursor cur = cls.Search(flt, false);
            IFeature feat = cur.NextFeature();
            cur = null;
            return feat;
        }

        /// <summary>
        /// 将要素对象转成图幅对象
        /// </summary>
        /// <param name="feature">要转换的要素</param>
        /// <param name="layername">要素类名称</param>
        /// <returns></returns>
        private static Mapsheet FeatureToSheet(IFeature feature, string layername)
        {
            Mapsheet sheet = new Mapsheet()
            {
                Code = feature.get_Value(_Indexes[layername][SheetHelper.F_MapNo]).ToString(),
                Checked = true,
                Geometry = feature.ShapeCopy
            };
            List<string> codes = new List<string>();
            foreach (KeyValuePair<string, int> kvp in _Indexes[layername])
            {
                if (kvp.Value < 0) continue;
                if (kvp.Key.Equals(SheetHelper.F_MapNo)) continue;
                object o = feature.get_Value(kvp.Value);
                if (o == null) continue;
                codes.Add(kvp.Key + ":" + o.ToString());
            }
            sheet.UserData = codes;
            return sheet;
        }
        
        /// <summary>
        /// 检查比例尺下的图号是否正确
        /// </summary>
        /// <param name="th"></param>
        /// <param name="scale"></param>
        /// <returns></returns>
        public static bool CheckCode(string th, int scale)
        {
            Mapsheet st = GetSheetByCode(th, scale);
            if (st == null) return false;
            return true;
        }

        /// <summary>
        /// 根据图号获取指定的图幅，周边图号取UserData属性
        /// </summary>
        /// <param name="th"></param>
        /// <param name="scale"></param>
        /// <returns></returns>
        public static Mapsheet GetSheetByCode(string th, int scale)
        {
            IFeatureClass cls = GetFeatureClass(scale);
            IFeature feat = GetFeatureByCode(cls, th);
            if (feat == null || feat.Shape.IsEmpty) return null;
            Mapsheet st = FeatureToSheet(feat, (cls as IDataset).Name);
            return st;
        }
        
        /// <summary>
        /// 根据空间对象与比例尺获取图幅
        /// </summary>
        /// <param name="geometry">空间对象</param>
        /// <param name="scale">比例尺</param>
        /// <returns></returns>
        public static List<Mapsheet> GetSheetByGeometry(IGeometry geometry, int scale)
        {
            IFeatureClass cls = GetFeatureClass(scale);
            if (cls == null) return null;
            ISpatialFilter flt = new SpatialFilter();
            flt.Geometry = geometry;
            flt.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
            flt.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;
            IFeatureCursor cur = cls.Search(flt, false);
            IFeature feat = null;
            List<Mapsheet> sheets = new List<Mapsheet>();
            string name = (cls as IDataset).Name;
            while ((feat = cur.NextFeature()) != null)
            {
                if (feat == null || feat.Shape.IsEmpty) continue;
                sheets.Add(FeatureToSheet(feat, name));
            }
            return sheets;
        }
        
        /// <summary>
        /// 根据图号与比例尺获取图幅
        /// </summary>
        /// <param name="ths">图号集合</param>
        /// <param name="scale">比例尺</param>
        /// <returns></returns>
        public static List<Mapsheet> GetSheetByCodes(List<string> ths, int scale)
        {
            IFeatureClass cls = GetFeatureClass(scale);
            if (cls == null) return null;
            string where = "";
            foreach (string s in ths)
            {
                where += " or  图号='" + s + "' ";
            }
            where = where.Trim().Substring(2);
            ISpatialFilter flt = new SpatialFilter();
            flt.WhereClause = where;
            flt.SearchOrder = esriSearchOrder.esriSearchOrderAttribute;
            IFeatureCursor cur = cls.Search(flt, false);
            IFeature feat = null;
            string name = (cls as IDataset).Name;
            List<Mapsheet> sheets = new List<Mapsheet>();
            while ((feat = cur.NextFeature()) != null)
            {
                if (feat == null || feat.Shape.IsEmpty) continue;
                sheets.Add(FeatureToSheet(feat, name));
            }
            return sheets;
        }

        /// <summary>
        /// 根据点获取图幅对象
        /// </summary>
        /// <param name="x">x坐标</param>
        /// <param name="y">y坐标</param>
        /// <param name="scale">比例尺</param>
        /// <returns></returns>
        public static Mapsheet GetMapsheet(double x, double y, int scale)
        {
            IFeatureClass cls = GetFeatureClass(scale);
            if (cls == null) return null;
            ISpatialFilter flt = new SpatialFilter();            
            IPoint p = new Point() { X = x, Y = y };
            flt.Geometry = (p as ITopologicalOperator).Buffer(0.02);
            flt.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
            flt.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;
            IFeatureCursor cur = cls.Search(flt, false);
            IFeature feat = null;
            string name = (cls as IDataset).Name;
            while ((feat = cur.NextFeature()) != null)
            {
                if (!feat.Shape.IsEmpty)
                {
                    if (feat.Shape.Envelope.XMin <= x && feat.Shape.Envelope.XMax >= x &&
                        feat.Shape.Envelope.YMin <= y && feat.Shape.Envelope.YMax >= y)
                        break;
                    if (feat.Shape.Envelope.XMin == x && feat.Shape.Envelope.YMin == y)
                        break;
                }
            }
            if (feat == null || feat.Shape.IsEmpty) return null;
            return FeatureToSheet(feat, name);
        }

        /// <summary>
        /// 获取图幅集合的范围
        /// </summary>
        /// <param name="sheets">图幅集合对象</param>
        /// <returns></returns>
        public static IPolygon GetSheetRegion(List<Mapsheet> sheets)
        {
            IPolygon allPolygon = new Polygon() as IPolygon;
            ITopologicalOperator topo = allPolygon as ITopologicalOperator;
            IGeometryCollection geoCol = new GeometryBag() as IGeometryCollection;
            object Missing = Type.Missing;
            foreach (Mapsheet Mapsheet in sheets)
            {
                IPolygon polygon = ((ESRI.ArcGIS.esriSystem.IClone)Mapsheet.Geometry).Clone() as IPolygon;
                if (polygon == null) continue;
                geoCol.AddGeometry(polygon, ref Missing, ref Missing);
            }
            if (geoCol.GeometryCount == 0) return allPolygon;
            topo.ConstructUnion(geoCol as IEnumGeometry);
            topo.Simplify();
            if (sheets.Count > 0 && sheets[0].Geometry.SpatialReference != null)
            {
                allPolygon.Project(sheets[0].Geometry.SpatialReference);
            }
            return allPolygon;
        }

        /// <summary>
        /// 获取图幅的所有边界线
        /// </summary>
        /// <param name="sheets">图幅集合对象</param>
        /// <returns></returns>
        public static IPolyline GetPolylineAll(List<Mapsheet> sheets)
        {
            if (sheets == null || sheets.Count == 0) return null;
            IPolyline polyline = new Polyline() as IPolyline;            
            IGeometryCollection geometrys = new GeometryBag() as IGeometryCollection;
            foreach (Mapsheet Mapsheet in sheets)
            {
                IPolygon polygon = ((IClone)Mapsheet.Geometry).Clone() as IPolygon;
                ITopologicalOperator2 topo2 = polygon as ITopologicalOperator2;
                topo2.IsKnownSimple_2 = false;
                topo2.Simplify();
                if (polygon == null || polygon.IsEmpty) continue;
                geometrys.AddGeometry(topo2.Boundary);
            }
            if (geometrys.GeometryCount == 0) return polyline;
            ITopologicalOperator2 topo = polyline as ITopologicalOperator2;
            topo.ConstructUnion(geometrys as IEnumGeometry);
            topo.IsKnownSimple_2 = false;
            topo.Simplify();
            polyline.Project(sheets[0].Geometry.SpatialReference);
            return polyline;
        }

        /// <summary>
        /// 获取图幅外围边界线
        /// </summary>
        /// <param name="sheets">图幅集合对象</param>
        /// <returns></returns>
        public static IPolyline GetPolylineOuter(List<Mapsheet> sheets)
        {
            if (sheets == null || sheets.Count == 0) return null;
            IPolygon polygon = GetSheetRegion(sheets);
            ITopologicalOperator2 topo = polygon as ITopologicalOperator2;
            topo.IsKnownSimple_2 = false;
            topo.Simplify();
            return topo.Boundary as IPolyline;
        }

        /// <summary>
        /// 获取图幅外部边界线，要求相邻的平行或垂直线连成一根
        /// </summary>
        /// <param name="sheets">图幅集合对象</param>
        /// <returns></returns>
        public static List<IPolyline> GetPolylineOuter2(List<Mapsheet> sheets)
        {
            return GetPolylines(GetPolylineOuter(sheets));
        }

        /// <summary>
        /// 获取图幅内部边界线
        /// </summary>
        /// <param name="sheets">图幅集合对象</param>
        /// <returns></returns>
        public static IPolyline GetPolylineInner(List<Mapsheet> sheets)
        {
            if (sheets == null || sheets.Count == 0) return null;
            IPolyline lineAll = GetPolylineAll(sheets);
            IPolyline lineOuter = GetPolylineOuter(sheets);
            ITopologicalOperator2 topo = lineAll as ITopologicalOperator2;
            topo.IsKnownSimple_2 = false;
            topo.Simplify();
            return topo.Difference(lineOuter) as IPolyline;
        }
        
        /// <summary>
        /// 获取图幅内部边界线，要求相邻的平行或垂直线连成一根
        /// </summary>
        /// <param name="sheets">图幅集合对象</param>
        /// <returns></returns>
        public static List<IPolyline> GetPolylineInner2(List<Mapsheet> sheets)
        {
            return GetPolylines(GetPolylineInner(sheets));
        }
        
        /// <summary>
        /// 通过迭代的方式找到横向和纵向分割线的集合
        /// </summary>
        /// <param name="sheetline">图幅边界线</param>
        /// <returns></returns>
        public static List<IPolyline> GetPolylines(IPolyline sheetline)
        {
            List<IPolyline> polylinesRst = new List<IPolyline>();
            if (sheetline == null || sheetline.IsEmpty) return null;
            IGeometryCollection polylineParts = sheetline as IGeometryCollection;
            if (polylineParts == null || polylineParts.GeometryCount == 0) return null;
            List<IPolyline> polylines = new List<IPolyline>();
            for (int i = 0; i < polylineParts.GeometryCount; i++)
            {
                IPolyline polyline = Convertor.ConvertLow2High(polylineParts.get_Geometry(i)) as IPolyline;
                if (sheetline.SpatialReference != null) polyline.Project(sheetline.SpatialReference);
                polylines.Add(polyline);
            }
            if (polylines == null || polylines.Count == 0) return null;
            while (polylines.Count > 0)
            {
                IPolyline polyline = polylines[0];
                polylines.RemoveAt(0);
                IPolyline PolylineRst = GetPolyLine(polyline, ref polylines);
                polylinesRst.Add(PolylineRst);
            }
            return polylinesRst;
        }

        /// <summary>
        /// 通过一根线，找到与它先邻接且方向一致的线的集合
        /// </summary>
        /// <param name="polyline">目标线</param>
        /// <param name="polylines">要查找的集合</param>
        /// <returns></returns>
        private static IPolyline GetPolyLine(IPolyline polyline, ref List<IPolyline> polylines)
        {
            IPolyline polylineRst = GetLine(polyline, polyline.FromPoint, ref polylines);
            ITopologicalOperator2 pTop = polylineRst as ITopologicalOperator2;
            pTop.IsKnownSimple_2 = false;
            pTop.Simplify();
            polylineRst = pTop.Union(GetLine(polyline, polyline.ToPoint, ref polylines)) as IPolyline;
            return polylineRst;
        }

        /// <summary>
        /// 迭代找线
        /// </summary>
        /// <param name="polyline">目标线</param>
        /// <param name="point">目标线的起止点</param>
        /// <param name="polylines">要查找的集合</param>
        /// <returns></returns>
        private static IPolyline GetLine(IPolyline polyline, IPoint point, ref List<IPolyline> polylines)
        {
            if (polylines == null || polylines.Count == 0) return polyline;
            IPolyline polylineRst = polyline;
            IRelationalOperator rel = point as IRelationalOperator;
            ITopologicalOperator2 top = polyline as ITopologicalOperator2;
            top.IsKnownSimple_2 = false;
            top.Simplify();
            ILine line = new Line();
            line.FromPoint = polyline.FromPoint;
            line.ToPoint = polyline.ToPoint;
            double angle = line.GetAngle();
            foreach (IPolyline value in polylines)
            {                
                if (!rel.Touches(value)) continue;//如果pPoint与value不是Touches的关系，则说明这两条线肯定不相邻
                IPoint pntFrom = value.FromPoint;
                IPoint pntTo = value.ToPoint;
                ILine lineTemp = new LineClass();
                lineTemp.FromPoint = pntFrom;
                lineTemp.ToPoint = pntTo;
                double angleTemp = lineTemp.GetAngle();               
                if (Math.Abs(angle - angleTemp) > 45) continue; //如果两者不是同为水平或垂直，那么它们与x轴的角度差应在90°左右
                Application.DoEvents();
                polylines.Remove(value);
                ITopologicalOperator2 top2 = value as ITopologicalOperator2;
                top2.IsKnownSimple_2 = false;
                top2.Simplify();
                polylineRst = top.Union(value) as IPolyline;
                top = polylineRst as ITopologicalOperator2;
                top.IsKnownSimple_2 = false;
                top.Simplify();
                if (GeometryHelper.IsSamePnt(point, pntFrom))
                    polylineRst = GetLine(polylineRst, pntTo, ref polylines);
                else if (GeometryHelper.IsSamePnt(point, pntTo))
                    polylineRst = GetLine(polylineRst, pntFrom, ref polylines);                
                break;//如果找到与该段相邻接的，则无需再找下去了
            }
            return polylineRst;
        }

    }

    ///// <summary>
    ///// 方位
    ///// </summary>
    //[Description("方位")]
    //public enum zjgisPosition
    //{
    //    /// <summary>
    //    /// 东北（右上）
    //    /// </summary>
    //    [Description("东北（右上）")]
    //    EastNorth,
    //    /// <summary>
    //    /// 东（右）
    //    /// </summary>
    //    [Description("东（右）")]
    //    East,
    //    /// <summary>
    //    /// 东南（右下）
    //    /// </summary>
    //    [Description("东南（右下）")]
    //    EastSourth,
    //    /// <summary>
    //    /// 南（下）
    //    /// </summary>
    //    [Description("南（下）")]
    //    Sourth,
    //    /// <summary>
    //    /// 西南（左下）
    //    /// </summary>
    //    [Description("西南（左下）")]
    //    WestSourth,
    //    /// <summary>
    //    /// 西（左）
    //    /// </summary>
    //    [Description("西（左）")]
    //    West,
    //    /// <summary>
    //    /// 西北（左上）
    //    /// </summary>
    //    [Description("西北（左上）")]
    //    WestNorth,
    //    /// <summary>
    //    /// 北（上）
    //    /// </summary>
    //    [Description("北（上）")]
    //    North        
    //}
}
